type Component
    Computed[Node];  // Observable<VNode>

function StaticComponent:
    &(&() => Node) => Component
    native 'ui-static-component';

const Empty: Component :=
    { StaticComponent &() =>
        { Node 'div' }
            . { with { Styles [('display','none')] } } };

export function Element:
    &(Tag, List[Component]) => Component
    &(tag, children) =>
        if ((children.{length}) = 0):
            { StaticComponent &() => { Node tag } },
        else:
            { Node (tag, children) };

export function Container:
    &(List[Component]) => Component
    &(children) => { Node ('div', children) };

export function StyledContainer:
    &(List[String]) => &(List[Component]) => Component
    &(@class) => &(children) => { Container children }.{ with {Style @class} };

export function Label:
    &(String) => Component
    &(text) => { StaticComponent &() => { Node ('div', text) } };

export function Label:
    &(Computed[String]) => Component
    &(@text) => { map (@text, (&(text) => { Node ('div', text) })) };

export function Input:
    &(Reactive[String]) => Component
    &(@text) =>
        let on-input := { EventHandler
            (@text, &(ev) => (ev get-string 'webuiValue')) },
        \ text := map { watch @text },
        { Node 'input' }
            . { with { Attrs [('type','text'),('value',text)] } }
            . { with { Events [('input',on-input)] } };

export function Checkbox:
    &(Reactive[Bool]) => Component
    &(@checked) =>
        let on-change := { EventHandler
            (@checked, &(ev) => (ev get-bool 'webuiChecked')) },
        \ checked := map { watch @checked },
        { Node 'input' }
            . { with { Attrs
                if checked:
                    ([('type','checkbox'), ('checked','checked')]),
                else:
                    ([('type','checkbox')])
            } }
            . { with { Events
                [ ('change',on-change) ]
            } };

export function Select:
    &(Reactive[String], List[(String,String)]) => Component
    &(@current, options) =>
        let on-change := { EventHandler
            (@current, &(ev) => (ev get-string 'webuiValue')) },
        let children := (options map &(name,desc) =>
            { Node ('option', desc) }
                . { with { Attrs [('value',name)] } }),
        \ current := map { watch @current },
        { Node 'select' }
            . { with { Attrs [('value',current)] } }
            . { with { Events [('change',on-change)] } }
            . { with { Content children } };

export function Button:
    &(String, Async) => Component
    &(text, action) =>
        let handler := { trigger action }.{EventHandler},
        { StaticComponent &() =>
            { Node ('button', text) }
                . { with { Events [('click',handler)] } } };

export function ListView:[T]
    &(Tag, Reactive[FlexList[T]], &(FlexListKey,Computed[Number],Reactive[T]) => Component) => Component
    &(tag, list, f) =>
        let children := (list consume &(item) => { f(item) }),
        { Node (tag, children) };

export function ListView:[T]
    &(Reactive[FlexList[T]], &(FlexListKey,Computed[Number],Reactive[T]) => Component) => Component
    &(list, f) => { ListView ('div', list, f) };

